home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume14 / 3bconnect next >
Encoding:
Internet Message Format  |  1988-05-08  |  53.3 KB

  1. Subject:  v14i085:  3B2 Ethernet Connection and File Transfer Utility
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Dave Settle <mcvax!oscar.smb.co.uk!dave@uunet.uu.net>
  7. Posting-number: Volume 14, Issue 85
  8. Archive-name: 3bconnect
  9.  
  10. [  I don't have a 3B, so you're on your own.  --r$  ]
  11.  
  12. This set is called 'connect', and is a utility for AT&T 3B2
  13. 3BNET (ethernet) networks, providing a remote login facility, and a
  14. file transfer capability.
  15.  
  16.     Dave.
  17. -----------------------------------CUT HERE-----------------------------------
  18. #! /bin/sh
  19. # This is a shell archive, meaning:
  20. # 1. Remove everything above the #! /bin/sh line.
  21. # 2. Save the resulting text in a file.
  22. # 3. Execute the file with /bin/sh (not csh) to create:
  23. #    README
  24. #    Makefile
  25. #    connect.1
  26. #    rcp.1
  27. #    server.c
  28. #    connect.c
  29. #    rcp.c
  30. #    eth.c
  31. #    ipaddr.c
  32. #    talk.c
  33. #    sub.c
  34. #    file.c
  35. #    terminate.c
  36. #    mynode.c
  37. #    ftp.c
  38. #    ni.h
  39. #    ftp.h
  40. export PATH; PATH=/bin:/usr/bin:$PATH
  41. echo shar: "extracting 'README'" '(4159 characters)'
  42. if test -f 'README'
  43. then
  44.     echo shar: "will not over-write existing file 'README'"
  45. else
  46. cat << \SHAR_EOF > 'README'
  47. AT&T 3B2 Ethernet Connection and File Transfer Utility
  48.  
  49. This package allows you to login to other 3B2 computers connected via 3Bnet, 
  50. in a similar fashion to 'cu', and also to transfer files across the ethernet.
  51.  
  52. CONNECT
  53.  
  54. 'Connect' provides a simple connection to a shell on the remote machine, linked
  55. to your terminal via pipes. Initially, YOUR terminal will be doing the echoing
  56. (in contrast to 'cu'), with lines shipped across the ethernet only when you
  57. hit RETURN.
  58.  
  59. You can force the connection into 'raw' mode (each character shipped as typed,
  60. remote end doing the echoing) by sending a QUIT signal. This allows you to
  61. use screen editors on the remote machine.
  62. A further QUIT signal gets you back into 'cooked' mode - this is indicated by
  63. '[cooked]' appearing on your terminal. Unfortunately, this means that you
  64. can't send QUIT signals to processes which you start on the remote host.
  65. Sorry about this. Interrupts get processed OK, though.
  66.  
  67. You close the connection by typing ^D at your terminal (in cooked mode) -
  68. if you want to send ^D to the remote end, you MUST be in 'raw' mode.
  69.  
  70. The big problem is that your remote shell has stdin, stdout, and stderr 
  71. connected to PIPES, rather than a real tty, which can cause certain commands
  72. (e.g. stty) to fail.
  73.  
  74. PC-INTERFACE.
  75. If you have PC-INTERFACE installed, then you can connect to one of the 
  76. pseudo-tty devices provided by the driver, by using the '-l' option of 'connect'
  77. This gets you a remote shell connected to a 'real' tty device, and 
  78. automatically puts you in 'raw' mode.
  79.  
  80. You close this type of connection by sending a QUIT signal.
  81.  
  82.  
  83. RCP
  84. 'rcp' provides a simple, but effective, method of transferring files across
  85. the network. The syntax resembles 'uucp'.
  86.  
  87.  
  88.  
  89. WHAT YOU NEED TO DO TO INSTALL IT.
  90.  
  91. a) Find out the ethernet addresses of all machines on the network.
  92.     Use 'nitable', or the program 'mynode' provided.
  93.     
  94. b) Set up a map file '/usr/lib/ethernet.addr' which maps the names of
  95. your hosts to the rightmost 2 digits of their ethernet address. 
  96. These should be unique - if not, change the value of LSB in 'ni.h' 
  97. to use any pair of digits which are unique for your network.
  98.  
  99.  
  100. E.g. 
  101. oscar    80.00.10.30.18.f2        
  102. olive   80.00.10.30.0b.45
  103.  
  104. /usr/lib/ethernet.addr:
  105.  
  106.         oscar    f2
  107.         olive    45
  108.  
  109.  
  110. c) Set up an inittab entry to run the server.
  111.  
  112. e.g. (assuming server is '/usr/lib/server')
  113.  
  114.     et:2:respawn:/usr/lib/server
  115.     
  116. It's important that this line is properly defined! 
  117. DO NOT DEFINE ANY INPUT OR OUTPUT FILES FOR THIS PROCESS.
  118.  
  119.  
  120. d) Make sure that you have enough 'port' structures allocated in the NI driver.
  121.    You can do this through the 'packagemgmt/3bnet/nidriver" option in 'sysadm'
  122.     I have 20 ports, 1 buffer per port, 1514 buffer size - this allows
  123.     me at least 5 concurrent 'connects' (I've not tried more)
  124.    You have to reboot for the reconfig to become effective.
  125.    
  126. e) Find out your value of NPROC, from the file /etc/master.d/KERNEL
  127.    Define this value in 'ni.h'
  128.     
  129.  
  130. f) To connect, say 'connect <host>'
  131.  
  132. e.g.
  133.     connect olive
  134.     
  135.  
  136.  
  137. BUGS
  138.  
  139. TTY:
  140. You're not connected to a tty at the remote end, so certain commands aren't
  141. going to work. Screen editors will work, but only if you use them when you're
  142. in 'raw' mode.
  143.  
  144. EOT:
  145. There's no way to send EOT to a remote process. Typing ^D on your terminal will
  146. close the connection. However, when you do a close, all your remote processes
  147. get sent a hangup signal, which usually amounts to the same thing.
  148.  
  149. ETHERNET ADDRESS:
  150. The ethernet addresses which are used by this package are NOT guaranteed to
  151. be unique, and may interfere with other ethernet applications. (3Bnet and
  152. PC-Interface seem to be OK, though).
  153.  
  154. If you get conflicts with other packages, you might have to change the values
  155. in server[] and client[] in 'ni.h' ([1] and [2])
  156.  
  157. They are currently set to 0x02 0x02 - this is totally arbitrary,
  158. so feel free to change it as you wish.
  159. The rest of the bytes in server[] and client[] will be OK.
  160.  
  161. REMOTE SHELL TERMINATION:
  162. If the remote shell dies, for any reason, you won't be informed until you 
  163. attempt to send it some input.
  164.  
  165. ADDING FEATURES.
  166. If you put any fancy bits in, please let me know!
  167.  
  168. Good luck!
  169.  
  170. Dave Settle, dave@smb.co.uk
  171. SHAR_EOF
  172. if test 4159 -ne "`wc -c < 'README'`"
  173. then
  174.     echo shar: "error transmitting 'README'" '(should have been 4159 characters)'
  175. fi
  176. fi
  177. echo shar: "extracting 'Makefile'" '(1042 characters)'
  178. if test -f 'Makefile'
  179. then
  180.     echo shar: "will not over-write existing file 'Makefile'"
  181. else
  182. cat << \SHAR_EOF > 'Makefile'
  183. #CFLAGS = -g
  184. #LIBS = -lg
  185. #
  186. # If you wish a SECURE version (no super-user server processes),
  187. # define SECURE. This will prevent root from using 'connect' or 'rcp'.
  188. #
  189. # CFLAGS = -O -DSECURE
  190.  
  191. SERVER = server.o eth.o ipaddr.o talk.o sub.o file.o ftp.o
  192. CONNECT = connect.o eth.o terminate.o ipaddr.o
  193. MYNODE = mynode.o ipaddr.o
  194. RCP = rcp.o sub.o ipaddr.o eth.o ftp.o
  195.  
  196. DOCS = README Makefile connect.1 rcp.1
  197.  
  198. SOURCES = server.c connect.c rcp.c eth.c ipaddr.c talk.c \
  199.     sub.c file.c terminate.c mynode.c ftp.c ni.h ftp.h
  200.  
  201. all: server connect mynode rcp
  202.  
  203. lint: 
  204.     lint ${SERVER:.o=.c}
  205.     lint ${CONNECT:.o=.c}
  206.     lint ${RCP:.o=.c}
  207.  
  208. ${SERVER} ${CONNECT} ${RCP} : ni.h
  209.  
  210. rcp.o ftp.o file.o : ftp.h
  211.  
  212. server: ${SERVER}
  213.     cc -o server ${SERVER} ${LIBS}
  214.     
  215. connect: ${CONNECT}
  216.     cc -o connect ${CONNECT} ${LIBS}
  217.     
  218. rcp: ${RCP}
  219.     cc -o rcp ${RCP} ${LIBS}
  220.  
  221. mynode: ${MYNODE}
  222.     cc -o mynode ${MYNODE} ${LIBS}
  223.  
  224. shar:
  225.     shar -cv ${DOCS} ${SOURCES} > connect.shar
  226.  
  227. install:
  228.     if [ -f /usr/lib/server ] ; then \
  229.         mv /usr/lib/server /usr/lib/server.old ; fi
  230.     cp server /usr/lib
  231. SHAR_EOF
  232. if test 1042 -ne "`wc -c < 'Makefile'`"
  233. then
  234.     echo shar: "error transmitting 'Makefile'" '(should have been 1042 characters)'
  235. fi
  236. fi
  237. echo shar: "extracting 'connect.1'" '(3547 characters)'
  238. if test -f 'connect.1'
  239. then
  240.     echo shar: "will not over-write existing file 'connect.1'"
  241. else
  242. cat << \SHAR_EOF > 'connect.1'
  243. .TH CONNECT 1 local
  244. .SH NAME
  245. connect
  246. .SH SYNOPSIS
  247. .B "connect [-l] remote_host"
  248. .PP
  249. .SH DESCRIPTION
  250. .I connect
  251. allows you to connect to a remote host connected via 3BNET, and to start a
  252. remote shell on that machine. The shell started is the one specified by 
  253. your SHELL variable, or "/bin/sh" if this is not set.
  254. .PP
  255. Initially, all the echoing is done by the local machine, and input is 
  256. transmitted line by line (cooked mode). If you wish the remote machine to
  257. do the echoing (e.g. you're running a screen editor there), you must put
  258. your terminal in raw mode, by sending a QUIT signal.
  259. .PP
  260. In raw mode, each character is send as soon as it is typed, and no echoing
  261. is performed by the local machine. A further QUIT signal will revert to
  262. cooked mode again (i.e. QUIT can be used to toggle between raw and cooked modes)
  263. .PP
  264. The connection can be closed by typing EOT in cooked mode.
  265. .PP
  266. .SH "LOGIN OPTION"
  267. .PP
  268. If the \fB-l\fR option is specified, and if PC-INTERFACE is installed on the
  269. remote host, then you will be connected to one of the pseudo-ttys supported
  270. by driver.
  271. .PP
  272. This option gives you a remote connection which looks more like a direct
  273. tty link, and automatically puts you in 'raw' mode. 
  274. .PP
  275. On the other hand, you have to go through all the bother of logging on,
  276. which takes more time.
  277. .PP
  278. Using the \fB-i\fR option \fBonly\fR, the connection is closed by a QUIT
  279. signal.
  280. .SH SECURITY
  281. The remote server will only accept a request to start a shell if your login
  282. name is known on the remote machine, and your remote uid matches your local uid.
  283. .PP
  284. The uid used to start the remote process is your \fBreal\fR uid, not your
  285. effective uid, so that even if you're running su(1), your remote process
  286. will not be root.
  287. .PP
  288. The only way you can login to a remote machine as root, is to login to your
  289. local machine as root (on the console), and \fIconnect\fR from there.
  290. .PP
  291. When the remote process it started, it will have your uid, the gid assosciated
  292. with the \fBremote\fR uid, and will be located in your remote home directory.
  293. .SH "HOST MAPPING"
  294. The mapping between host names and ethernet addresses is defined in the
  295. file '/usr/lib/ethernet.addr'.
  296. .PP
  297. The format of the file is:
  298. .PP
  299.         <hostname> <ID>
  300. .PP
  301. e.g.
  302. .PP
  303.         olive    45
  304.         oscar    f2
  305. .PP
  306. As shipped, the package expects each host to have a physical ethernet
  307. address which is unique \fBin the last (lsb) byte\fR.
  308. .PP
  309. The addresses which are used are:
  310. .PP
  311.         XX.YY.YY.00.00.HH    Server
  312.         XX.YY.YY.PID.PID.HH    Client/Remote Process.
  313. .PP
  314. The values of XX and YY are totally arbitrary, and you can change them if
  315. you wish. The value of HH must be different for each host, and is taken from
  316. the LSB of the host physical ethernet address. If this does not produce
  317. a unique value for each server, then you can choose to take it from any other
  318. byte of the physical address, by redefining LSB (ni.h) to pick any other byte.
  319. .SH EXAMPLES
  320. .PP
  321. connect -l olive
  322. .PP
  323. Connect to a PC-INTERFACE pseudo-tty on host 'olive'.
  324. .PP
  325. connect oscar
  326. .PP
  327. Connect to a remote shell on host 'oscar'
  328. .PP
  329. .SH FILES
  330. /dev/ni                Ethernet interface.
  331. .PP
  332. /dev/ptty??            PC-INTERFACE pseudo-ttys
  333. .PP
  334. /usr/lib/ethernet.add        List of hosts.
  335. .PP
  336. .SH SEE ALSO
  337. .SH BUGS
  338. You can't send QUIT signals to remote processes, 'cos QUIT does various things
  339. to the connection.
  340. .PP
  341. The values of XX and YY may conflict with other applications.
  342. .PP
  343. There's no guarantee that you can get a unique host ID using only one byte
  344. from the physical node address.
  345. .PP
  346. \fIconnect <host>\fR gets you a shell which isn't connected to a real tty
  347. device.
  348. SHAR_EOF
  349. if test 3547 -ne "`wc -c < 'connect.1'`"
  350. then
  351.     echo shar: "error transmitting 'connect.1'" '(should have been 3547 characters)'
  352. fi
  353. fi
  354. echo shar: "extracting 'rcp.1'" '(2176 characters)'
  355. if test -f 'rcp.1'
  356. then
  357.     echo shar: "will not over-write existing file 'rcp.1'"
  358. else
  359. cat << \SHAR_EOF > 'rcp.1'
  360. .TH RCP 1 local
  361. .SH SYNOPSIS
  362. .B "rcp [-d] source [source ...] dest"
  363. .SH DESCRIPTION
  364. \fIrcp\fR allows files to be transferred to or from remote machines,
  365. using the ethernet connection between Olivetti 3B2's supporting 3BNET.
  366. It does \fBNOT\fR use the \fInisend\fB network interface, which has 
  367. proved to be very unreliable, but uses the packet transport interface
  368. directly.
  369. .PP
  370. Any of the file names specified may be prefixed with a \fBsystem name\fR,
  371. meaning that the file should be transferred to or from the remote machine
  372. specified. The system name is separated from the file name by either
  373. a '!' or a ':' character.
  374. .PP
  375. Only one remote system may be specified for each \fIrcp\fR command, 
  376. and all the source files must live on the same system. It is not possible
  377. to move files from one place to another on a remote machine - they
  378. must transfer across the ethernet connection. This is a restriction 
  379. imposed by the protocol.
  380. .SH FLAGS
  381. .PP
  382. .TP
  383. .B -d
  384. Turn debugging output on during the file transfer procedure. Useful only
  385. if you know what the ftp protocol is supposed to be doing.
  386. .PP
  387. .SH EXAMPLES
  388. .PP
  389. rcp oscar!/usr/dave/file.c /usr/tmp
  390. .PP
  391. Copies the file \fB/usr/dave/file.c\fR from the system \fBoscar\fR to the
  392. destination \fB/usr/tmp\fR on the local system.
  393. .PP
  394. rcp /usr/dave/src/rcp.c myprog olive:/usr/dave/public
  395. .PP
  396. Copies the local files \fB/usr/dave/src/rcp.c\fR and
  397. \fBmyprog\fR, to the destination \fB/usr/dave/public\fR on the
  398. system \fBolive\fR.
  399. .SH "SEE ALSO"
  400. uucp(1), cp(1), nisend(1)
  401. .SH DIAGNOSTICS
  402. Normal information about files which can't be read or written; this
  403. information may come from either host.
  404. .PP
  405. In the event of a protocol failure, the remote server may terminate
  406. \fIrcp\fR. There should be a diagnostic message - consult your guru.
  407. .SH BUGS
  408. You can't talk to more than one remote host at a time: things like
  409. .PP
  410. rcp a:file1 b:file2 c:dest
  411. .PP
  412. are \fBNOT\fR allowed.
  413. .PP
  414. You can't move things around on the remote host - you can only copy
  415. from one host to another.
  416. .PP
  417. The file transfer protocol is a home-grown variety, and is not going
  418. to be any sort of standard. It would be nice if it used TCP/IP, or
  419. some other well-defined protocol.
  420. SHAR_EOF
  421. if test 2176 -ne "`wc -c < 'rcp.1'`"
  422. then
  423.     echo shar: "error transmitting 'rcp.1'" '(should have been 2176 characters)'
  424. fi
  425. fi
  426. echo shar: "extracting 'server.c'" '(6188 characters)'
  427. if test -f 'server.c'
  428. then
  429.     echo shar: "will not over-write existing file 'server.c'"
  430. else
  431. cat << \SHAR_EOF > 'server.c'
  432. /*
  433.  * Copyright (C) 1988 Dave Settle. All rights reserved.
  434.  * Permission is granted to use, copy and modify this software, providing
  435.  * that it is not sold for profit, and that this copyright notice is retained
  436.  * in any copies of the source.
  437.  */
  438. /*
  439.  * server: ethernet server. Accepts requests, and spawns processes to
  440.  * serve remote clients.
  441.  */
  442.  
  443. #include <sys/ni.h>
  444. #include <stdio.h>
  445. #include <sys/signal.h>
  446. #include <sys/errno.h>
  447. extern int errno;
  448. #include <pwd.h>
  449.  
  450. struct passwd *getpwnam();
  451. char *strrchr();
  452.  
  453. #define MAIN
  454. #include "ni.h"
  455.  
  456. int input[2], output[2];        /* pipes for shell */
  457. char cmd[128], program[32];
  458. int reader, sh;                /* pid of child reader process */
  459.  
  460. #define LOG  "/usr/lib/server.log"
  461.  
  462. #define GIGABYTE    2*1024*1024    /* number of blocks in 1 GByte */
  463.  
  464. /*
  465.  * endproc: called when the shell has died (via SIGPIPE) by the writer process
  466.  */
  467. endproc(){
  468.     struct request req;
  469.     skill(reader, SIGTERM);
  470.     sprintf(req.r_data, "your shell died");
  471.     send(&req, strlen(req.r_data), TERMINATE, client);
  472.     exit(0);
  473. }
  474. /*
  475.  * Called when we get a TERMINATE packet from the client.
  476.  */
  477. sigterm(){    
  478.     skill(reader, SIGTERM);
  479.     skill(sh, SIGHUP);
  480.     sleep(1);
  481.     skill(sh, SIGKILL);
  482.     exit(0);
  483. }
  484.  
  485. main()
  486. {
  487.     int in;
  488.     signal(SIGHUP, SIG_IGN);
  489.     signal(SIGINT, SIG_IGN);
  490.     signal(SIGQUIT, SIG_IGN);
  491.     signal(SIGTERM, sigterm);
  492.     ulimit(2, (long) GIGABYTE);
  493. /*
  494.  * This process runs from "init" - it doesn't have ANY file descriptors open
  495.  * Check this, and if necessary, create some dummy fd's.
  496.  */
  497.      if(in = open("/dev/null", 0) == 0) {
  498.          open("/dev/null", 1);
  499.          open("/dev/null", 1);
  500.      }
  501.      else close(in);
  502.     freopen(LOG, "a+", stderr);
  503.     if(configure(server, 2, 0) == -1) exit(1);
  504.      accept();
  505.      return(0);
  506. }
  507. accept(){
  508.     struct request request;
  509.      recv(&request);
  510. /*
  511.  * Fork off child to deal with the request. We die, and are respawned by
  512.  * init.
  513.  */
  514.      if(fork() == 0) exit(serve(&request));
  515. /*
  516.  * parent - exit.
  517.  */
  518.      return(0);
  519. }
  520. /*
  521.  * serve: set up a shell for the remote process, and pass data.
  522.  */
  523. #define RD 0
  524. #define WT 1
  525. serve(r)
  526. register struct request *r;
  527. {
  528.     int pid = getpid(), writer, n, uid, ok;
  529.     int oldnet;
  530.     char user[32];
  531.     struct passwd *pw;
  532.     char home[32], path[128], shell[16], mail[64], term[32], logname[64];
  533. /*
  534.  * Get the user info from the request header, and set our parameters
  535.  */    
  536.     n = sscanf(r->r_data, "%d %s %s", &uid, user, term);
  537.     memcpy(client, r->r_port.srcaddr, ETHERSIZE);
  538.     pw = getpwnam(user);
  539.     if(pw == NULL) {
  540.         n = sprintf(r->r_data, "User '%s' not known here", user);
  541.         send(r, n, TERMINATE, client);
  542.         exit(0);
  543.     }
  544.     if(pw->pw_uid != uid) {
  545.         n = sprintf(r->r_data, "User '%s' and uid '%d' don't match",
  546.             user, uid);
  547.         send(r, n, TERMINATE, client);
  548.         exit(0);
  549.     }
  550. #ifdef SECURE
  551.     if(uid == 0) {
  552.         n = sprintf(r->r_data, "Secure server: root access not allowed");
  553.         send(r, n, TERMINATE, client);
  554.         exit(0);
  555.     }
  556. #endif
  557.     setpgrp();            /* Set up a process group */
  558.     setgid(pw->pw_gid);
  559.     setuid(pw->pw_uid);
  560.     sprintf(mail, "MAIL=/usr/mail/%s", user);
  561.     sprintf(path, "PATH=:/usr/ucb:/bin:/usr/bin");
  562.     sprintf(logname, "LOGNAME=%s", user);
  563.     sprintf(shell, "SHELL=%s", pw->pw_shell);
  564.     sprintf(home, "HOME=%s", pw->pw_dir);
  565.     putenv(home); putenv(path); putenv(shell); 
  566.     putenv(mail); putenv(logname);
  567.     putenv(term);
  568.     chdir(pw->pw_dir);
  569. if(debug) {
  570.     fprintf(stderr, "Server request from %s\n", getenv("LOGNAME"));
  571. }
  572.     server[PIDMSB] = (pid >> 8) & 0xff;
  573.     server[PIDLSB] = pid & 0xff;
  574.     oldnet = ethernet;            /* save old port for a bit */
  575.     if(configure(server, 3, getpid()) == -1) {    /* We are this address */
  576.         ethernet = oldnet;        /* Use old port */
  577.         sprintf(r->r_data, "Can't allocate port");
  578.         send(r, strlen(r->r_data), TERMINATE, client);
  579.         exit(1);
  580.     }
  581.     close(oldnet);                /* OK - use the new port */
  582. /*
  583.  * Inform the client of our new address
  584.  */
  585.     memcpy(r->r_data, mynode, ETHERSIZE);
  586.     send(r, ETHERSIZE, ACCEPT, client);
  587. /*
  588.  * Get the command which the client wishes us to execute
  589.  */
  590.      recv(r);
  591.      memcpy(cmd, r->r_data, r->r_size);
  592. /*
  593.  * Special cases: 
  594.  *    "login" - user wants a real login - use the PC-Interface pttys
  595.  *    "fileserver" - user wants to transfer files.
  596.  */
  597.      if(!strcmp(cmd, "login")) return(dologin(r));
  598.      if(!strcmp(cmd, "fileserver")) return(fileserver(r));
  599. /*
  600.  * Now start off the child process, and collect it's output
  601.  */
  602.      pipe(input);
  603.      pipe(output);
  604.      if(sh = fork()) {
  605.          close(input[WT]);
  606.          close(output[RD]);
  607.          writer = getpid();
  608.          if(reader = fork()) {
  609.              ok = 1;
  610.              signal(SIGPIPE, endproc);
  611.              while(ok) {
  612.                  recv(r);
  613. if(debug) {
  614.                  fprintf(stderr, "server: got [%s] from %s\n",
  615.                      r->r_data, ipaddr(r->r_port.srcaddr));
  616. }
  617.                 ok = passon(r, output[WT]);
  618.              }
  619.             sigterm();
  620.          }
  621.          else {
  622.              signal(SIGTERM, SIG_DFL);
  623.              while((n = read(input[RD], r->r_data, sizeof r->r_data)) != -1) {
  624.                  send(r, n, DATA, client);
  625. if(debug) {
  626.                  fprintf(stderr, "server: %s sent to %s\n",
  627.                      r->r_data, ipaddr(client));
  628. }
  629.              }
  630.              sprintf(r->r_data, "hangup\n");
  631.              send(r, strlen(r->r_data), TERMINATE, client);
  632.              skill(writer, SIGKILL);
  633.              exit(0);
  634.          }
  635.      }
  636.      else doexec(r);        /* exec the child shell */
  637. }    
  638. /*
  639.  * doexec: perform exec of requested procedure
  640.  */
  641. doexec(r)
  642. struct request *r;
  643. {
  644.     char *argv[6];
  645.     split(cmd, argv);
  646.      close(0); close(1); close(2);
  647. /*
  648.  * Duplicate READ side of output as stdin
  649.  */
  650.      if(dup(output[RD]) != 0) perror("dup(input) != 0");
  651. /*
  652.  * Duplicate WRITE side of input as  stdout & stderr
  653.  */
  654.      if(dup(input[WT]) != 1) perror("dup(output) != 1");
  655.      if(dup(input[WT]) != 2) perror("dup(output) != 2");
  656. /*
  657.  * close all the parent's pipes - they're duplicated
  658.  */
  659.     close(input[0]); close(input[1]);
  660.     close(output[0]); close(output[1]);
  661.     close(ethernet);
  662.     signal(SIGINT, SIG_DFL);
  663.     signal(SIGQUIT, SIG_DFL);
  664.     execvp(program, argv);
  665.     sprintf(r->r_data, "Cannot exec %s\n", cmd);
  666.     send(r, strlen(r->r_data), DATA, client);
  667.     exit(1);
  668. }
  669. split(p, argv)
  670. register char *p;
  671. char **argv;
  672. {    
  673.     int n = 1;
  674.     char *f;
  675.     static char name[16];
  676.     argv[0] = p;
  677.     while(*p) {
  678.         if(*p == ' ') {
  679.             *p++ = '\0';
  680.             while(*p == ' ') p++;
  681.             argv[n++] = p;
  682.         }
  683.         else p++;
  684.     }
  685.     strcpy(program, argv[0]);
  686.     if(f = strrchr(argv[0], '/')) f++;
  687.     else f = argv[0];
  688.     sprintf(name, "-%s", f);
  689.     argv[0] = name;
  690.     argv[n++] = NULL;
  691. }
  692.  
  693. SHAR_EOF
  694. if test 6188 -ne "`wc -c < 'server.c'`"
  695. then
  696.     echo shar: "error transmitting 'server.c'" '(should have been 6188 characters)'
  697. fi
  698. fi
  699. echo shar: "extracting 'connect.c'" '(6204 characters)'
  700. if test -f 'connect.c'
  701. then
  702.     echo shar: "will not over-write existing file 'connect.c'"
  703. else
  704. cat << \SHAR_EOF > 'connect.c'
  705. /*
  706.  * Copyright (C) 1988 Dave Settle. All rights reserved.
  707.  * Permission is granted to use, copy and modify this software, providing
  708.  * that it is not sold for profit, and that this copyright notice is retained
  709.  * in any copies of the source.
  710.  */
  711. /*
  712.  * connect.c: connect tty to remote host.
  713.  */
  714.  
  715. #include <sys/types.h> 
  716. #include <sys/signal.h>
  717. #include <sys/errno.h>
  718. #include <sys/utsname.h>
  719. #include <termio.h>
  720. #include <pwd.h>
  721.  
  722. struct passwd *getpwuid(), *getpwnam();
  723. char *getlogin();
  724. int (*signal())();
  725.  
  726. extern int errno;
  727. #include <stdio.h>
  728.  
  729. #define MAIN
  730. #include "ni.h"
  731.  
  732. char remoteshell[] = {0,0,0,0,0,0};
  733.  
  734. struct request request;
  735.  
  736. int reader, writer;            /* Processes */
  737.  
  738. int login = 0;                /* Are we doing a login? */
  739.  
  740. #define COOKED 0
  741. #define RAW 1
  742. struct termio raw, cooked;        /* To allow screen mode changes */
  743. int screenmode = COOKED, toggle(), trap();
  744.  
  745. wakeup(){
  746.     signal(SIGALRM, wakeup);
  747. }
  748. /*
  749.  * Got a signal - die. Writer is child process here.
  750.  */
  751. die(sig){
  752.     if(sig) printf("Connection closed.\r\n");
  753.     if((getpid() == writer) && reader) kill(reader, SIGTERM);
  754.     else if(writer) terminate(writer);
  755.     send(&request, 0, TERMINATE, server);
  756.     if(cooked.c_oflag) ioctl(fileno(stdout), TCSETA, &cooked);
  757.     exit(0);
  758. }
  759.  
  760. sendsig(sig)
  761. {
  762.     struct request request;
  763.     signal(sig, sendsig);
  764.     if(sig == SIGINT) send(&request, 0, RMTSIGINT, server);
  765.     if(sig == SIGQUIT) send(&request, 0, RMTSIGQUIT, server);
  766. }
  767.     
  768.  
  769. main(argc, argv)
  770. char **argv;
  771.  
  772. {
  773.     struct utsname uts;
  774.     register char *sys = argv[argc - 1];
  775.     int i;
  776.     if(argc < 2) {
  777.         printf("usage: %s hostname\n", argv[0]);
  778.         exit(1);
  779.     }
  780.     uname(&uts);
  781.     for(i=1;i<argc;i++) if(*argv[i] == '-') switch(argv[i][1]) {
  782.     case 'l':
  783.         login = 1;
  784.         break;
  785.     case 'd':
  786.         debug = 1;
  787.         break;
  788.     }
  789.     signal(SIGALRM, wakeup);
  790.     signal(SIGTERM, SIG_IGN);
  791.     if(configure(client, 3, getpid()) == -1) exit(1);
  792.     if((i = hostaddr(argv[argc - 1])) == -1) {
  793.         printf("Host '%s' not known\n", argv[argc - 1]);
  794.         exit(1);
  795.     }
  796.     else server[NODE] = i;
  797.     connect(server, sys);
  798.     return(0);
  799. }
  800. /*
  801.  * connect(addr): set up connection to ethernet address 'addr' (system 'sys')
  802.  */
  803. connect(addr, sys)
  804. char *addr, *sys;
  805. {
  806.     register struct request *r = &request;
  807.     char *shell, *user;
  808.     int n;
  809.     struct passwd *pw;
  810. /*
  811.  * send off our login name and uid to remote host.
  812.  * also send timezone.
  813.  */
  814.      if((user = getlogin()) == NULL) 
  815.          pw = getpwuid(getuid());
  816.      else
  817.          pw = getpwnam(user);
  818.      if(pw == NULL) {
  819.          printf("Can't determine your user name!\n");
  820.          exit(1);
  821.      }
  822.      if(*pw->pw_name == 0) {
  823.          printf("Your user name is NULL! You seem to be %s\n",
  824.              user ? user : "unknown");
  825.          exit(1);
  826.      }
  827.     printf("Trying to connect to %s ... ", ipaddr(addr));
  828.      fflush(stdout);
  829.     sprintf(r->r_data, "%d %s TERM=%s", 
  830.         pw->pw_uid, pw->pw_name, getenv("TERM"));
  831.     n = strlen(r->r_data);
  832.     send(r, n, REQUEST, addr);
  833.     recv(r);
  834.      if(r->r_type == TERMINATE) {
  835.          printf("rejected!\n%s: %s\n", sys, r->r_data);
  836.          exit(0);
  837.      }
  838.     memcpy(server, r->r_port.srcaddr, ETHERSIZE);
  839. /*
  840.  * trap signals from here on, so that we can terminate the remote side.
  841.  */
  842.     signal(SIGQUIT, toggle);
  843.     signal(SIGINT, sendsig);
  844.     signal(SIGHUP, die);
  845.     printf("OK\nStarting remote %s ... ", login ? "login" : "shell");
  846.     fflush(stdout);
  847.     if(login) n = sprintf(r->r_data, "login");
  848.      else {
  849.          shell = getenv("SHELL");
  850.          if(shell == 0) shell = "/bin/sh";
  851.          n = sprintf(r->r_data, "%s -i", shell);
  852.     }
  853.      send(r, n, REQUEST, server);
  854.      recv(r);
  855.      if(r->r_type == TERMINATE) {
  856.          printf("rejected!\n%s: %s\n", sys, r->r_data);
  857.          exit(0);
  858.      }
  859.      printf("OK\nConnection complete ... server is %s\n", ipaddr(server));
  860. /*
  861.  * Set up the termio structures needed for mode swapping
  862.  */
  863.      ioctl(fileno(stdin), TCGETA, &cooked);
  864.      ioctl(fileno(stdin), TCGETA, &raw);
  865.      raw.c_oflag &= ~OPOST;
  866.      raw.c_cc[VMIN] = 1;
  867.      raw.c_cc[VTIME] = 0;
  868.      raw.c_iflag = 0;
  869.      raw.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL);
  870.      screenmode = COOKED;
  871. /*
  872.  * fork for reader and writer processes.
  873.  */
  874.     reader = getpid();
  875.      if(writer = fork()) {
  876. /*
  877.  * If we are starting a remote login via a ptty, then the remote host will
  878.  * echo, so set to raw mode. SIGQUIT will close the connection.
  879.  */
  880.          if(login) {
  881.              screenmode = RAW;
  882.             ioctl(fileno(stdin), TCSETA, &raw);
  883.             signal(SIGQUIT, die);
  884.             signal(SIGINT, trap);
  885.         }
  886.         else {
  887. /*
  888.  * Send the remote shell an initial newline, so that the user sees a prompt.
  889.  */
  890.             r->r_data[0] = '\n';
  891.             send(r, 1, DATA, server);
  892.         }
  893.          while(n = read(fileno(stdin), r->r_data, sizeof r->r_data)) {
  894.              if(n == -1) {
  895.                  if(errno == EINTR) continue;
  896.                  else break;
  897.              }
  898. if(debug) {
  899.             printf("client: sent [");
  900.             write(fileno(stdout), r->r_data, n);
  901.             printf("] to %s\n", ipaddr(server));
  902. }
  903.              send(r, n, DATA, server);
  904.          }
  905.          send(r, 0, TERMINATE, server);
  906.          terminate(writer);
  907.          ioctl(fileno(stdout), TCSETA, &cooked);
  908.          exit(0);
  909.      }
  910.     else {
  911.          signal(SIGINT, SIG_IGN);
  912.          signal(SIGQUIT, SIG_IGN);
  913.          while(1) {
  914.             recv(r);
  915. if(debug) {
  916.              printf("client: got [");
  917.             write(fileno(stdout), r->r_data, r->r_size);
  918.              printf("] from %s\n", ipaddr(r->r_port.srcaddr));
  919. }
  920.              if(r->r_type == TERMINATE) {
  921.                  printf("Lost remote connection [");
  922.                  fflush(stdout);
  923.                  write(fileno(stdout), r->r_data, r->r_size);
  924.                  printf("]\n");
  925.                 kill(reader, SIGTERM);
  926.                 ioctl(fileno(stdout), TCSETA, &cooked);
  927.                  exit(0);
  928.              }
  929.              write(fileno(stdout), r->r_data, r->r_size);
  930.          }
  931.      }
  932. }
  933. /*
  934.  * toggle: change screen mode from RAW <-> COOKED
  935.  */
  936. toggle(sig){
  937.     static int (*handler)();
  938.     signal(sig, toggle);
  939.     switch(screenmode) {
  940.     case COOKED:
  941.         handler = signal(SIGINT, trap);
  942.         ioctl(fileno(stdin), TCSETA, &raw);
  943.         putchar(07);        /* beep */
  944.         fflush(stdout);
  945.         screenmode = RAW;
  946.         break;
  947.     case RAW:
  948.         if(handler) signal(SIGINT, handler);
  949.         ioctl(fileno(stdin), TCSETA, &cooked);
  950.         printf("[cooked]");
  951.         fflush(stdout);
  952.         screenmode = COOKED;
  953.         break;
  954.     }
  955. }
  956. /*
  957.  * trap interrupts in raw mode, substitute 'del' char
  958.  * We can't just ignore interrupts, otherwise we can't switch out of raw mode.
  959.  */
  960. trap(sig){
  961.     struct request r;
  962.     signal(sig, trap);
  963.     switch(sig) {
  964.     default:
  965.     case SIGINT:
  966.         r.r_data[0] = cooked.c_cc[VINTR];
  967.         break;
  968.     case SIGQUIT:
  969.         r.r_data[0] = cooked.c_cc[VQUIT];
  970.         break;
  971.     }
  972.     send(&r, 1, DATA, server);
  973. }
  974.  
  975. SHAR_EOF
  976. if test 6204 -ne "`wc -c < 'connect.c'`"
  977. then
  978.     echo shar: "error transmitting 'connect.c'" '(should have been 6204 characters)'
  979. fi
  980. fi
  981. echo shar: "extracting 'rcp.c'" '(5486 characters)'
  982. if test -f 'rcp.c'
  983. then
  984.     echo shar: "will not over-write existing file 'rcp.c'"
  985. else
  986. cat << \SHAR_EOF > 'rcp.c'
  987. /*
  988.  * rcp.c: NOT the Berkeley version.
  989.  *
  990.  * rcp [system!]source ... [system!]dest
  991.  * e.g. rcp a!file1 b!file2 c!/usr/tmp
  992.  *
  993.  * AUTHOR: Dave Settle, THORN EMI SMB Business Software, June 86
  994.  *
  995.  * Electronic address:
  996.  *        dave%smb@ukc
  997.  *        dave@smb.co.uk
  998.  *
  999.  */
  1000. #include <stdio.h>
  1001. #include <errno.h>
  1002. #include <sys/types.h>
  1003. #include <sys/stat.h>
  1004. #include <sys/utsname.h>
  1005. #include <pwd.h>
  1006.  
  1007. #define MAIN
  1008. #include "ni.h"
  1009. #include "ftp.h"
  1010.  
  1011. extern int debug;
  1012.  
  1013. char *getenv(), *getlogin();
  1014. char *sysname(), *filename(), *lastpart();
  1015. struct passwd *getpwuid(), *getpwnam();
  1016. struct request request;
  1017. time_t time(), interval();
  1018. long kbytes();
  1019.  
  1020. die(sig){
  1021.     struct request req;
  1022.     if(sig) printf("\r\n%s: server terminated\n", 
  1023.         (sig == SIGINT) ? "Interrupt" : "Quit");
  1024.     send(&req, 0, TERMINATE, server);
  1025.     exit(1);
  1026. }
  1027.  
  1028. extern char *panicstr;
  1029. panic(sig){
  1030.     printf("server has terminated me: %s\n", 
  1031.         panicstr ? panicstr : "no reason");
  1032.     exit(0);
  1033. }
  1034.  
  1035. main(argc, argv)
  1036. int argc;
  1037. char **argv;
  1038. {
  1039. /*
  1040.  * check if the destination is local or remote.
  1041.  */
  1042.     int i, n, start = 1;
  1043.     struct utsname uts;
  1044.     char *user;
  1045.     struct passwd *pw;
  1046.     register struct request *r = &request;
  1047.     user = getenv("LOGNAME");
  1048.     if(user == NULL) printf("who are you?\n");
  1049. /*
  1050.  * find out node name
  1051.  */
  1052.      uname(&uts);
  1053.      strncpy(localnode, uts.nodename, sizeof uts.nodename);
  1054. #ifdef DEBUG
  1055.     printf("localnode: %s\n", localnode);
  1056. #endif
  1057.     hostname = NULL;
  1058.     for(i=1;i<argc;i++) {
  1059.         if(*argv[i] == '-') {
  1060.             switch(argv[i][1]) {
  1061.             case 'd':
  1062.                 debug = 1;
  1063.                 start = i + 1;
  1064.                 break;
  1065.             }
  1066.             continue;
  1067.         }
  1068.         if(remote(argv[i])) {
  1069.             if(hostname && strcmp(hostname, sysname(argv[i]))) {
  1070.                 printf("Cannot talk to %s AND %s!\n",
  1071.                     hostname, sysname(argv[i]));
  1072.                 exit(1);
  1073.             }
  1074.             else hostname = sysname(argv[i]);
  1075.         }
  1076.     }
  1077. /*
  1078.  * If this isn't really a remote copy, subsitute 'cp'
  1079.  */
  1080.     if(hostname == 0) {
  1081.         argv[0] = "cp";
  1082.         argv[argc] = 0;
  1083.         execv("/bin/cp", argv);
  1084.         exit(1);
  1085.     }
  1086.     if((i = hostaddr(hostname)) == -1) {
  1087.         printf("Host '%s' not known\n", hostname);
  1088.         exit(1);
  1089.     }
  1090.     else server[NODE] = i;
  1091.     if(configure(client, WINDOW, getpid()) == -1) {
  1092.         perror("/dev/ni");
  1093.         exit(1);
  1094.     }
  1095. /*
  1096.  * send off our login name and uid to remote host.
  1097.  * also send term type.
  1098.  */
  1099.      if((user = getlogin()) == NULL) 
  1100.          pw = getpwuid(getuid());
  1101.      else
  1102.          pw = getpwnam(user);
  1103.      if(pw == NULL) {
  1104.          printf("Can't determine your user name!\n");
  1105.          exit(1);
  1106.      }
  1107.      if(*pw->pw_name == 0) {
  1108.          printf("Your user name is NULL! You seem to be %s\n",
  1109.              user ? user : "unknown");
  1110.          exit(1);
  1111.      }
  1112.     printf("Trying to connect to %s ... ", ipaddr(server));
  1113.      fflush(stdout);
  1114.     sprintf(r->r_data, "%d %s TERM=%s", 
  1115.         pw->pw_uid, pw->pw_name, getenv("TERM"));
  1116.     n = strlen(r->r_data);
  1117.     send(r, n, REQUEST, server);
  1118.     recv(r);
  1119.      if(r->r_type != ACCEPT) {
  1120.          printf("rejected!\n%s: %s\n", hostname, r->r_data);
  1121.          exit(0);
  1122.      }
  1123.     memcpy(server, r->r_port.srcaddr, ETHERSIZE);
  1124.     printf("OK\nStarting remote file server ... "); fflush(stdout);
  1125. /*
  1126.  * trap signals from here on, so that we can terminate the remote side.
  1127.  */
  1128.     signal(SIGQUIT, die);
  1129.     signal(SIGINT, die);
  1130.     signal(SIGHUP, die);
  1131.     signal(SIGTERM, panic);
  1132.     n = sprintf(r->r_data, "fileserver");
  1133.     send(r, n, REQUEST, server);
  1134.     recv(r);
  1135.     if(r->r_type != ACCEPT) {
  1136.         printf("rejected!\n%s: %s\n", hostname, r->r_data);
  1137.         exit(1);
  1138.     }
  1139.     printf("OK\n");
  1140. /*
  1141.  * Now send (or receive) the files.
  1142.  */
  1143.     if(remote(argv[argc - 1])) 
  1144.         for(i=start;i<(argc - 1);i++) 
  1145.             sendit(filename(argv[i]), filename(argv[argc - 1]));
  1146.     else
  1147.         for(i=start;i<(argc - 1);i++) 
  1148.             getit(filename(argv[i]), filename(argv[argc - 1]));
  1149.     die(0);    
  1150.     /*NOTREACHED*/
  1151. }
  1152. /*
  1153.  * sendit: send local filename to remote server.
  1154.  */
  1155. sendit(local, remote)
  1156. char *local, *remote;
  1157. {
  1158.     register int n, f;
  1159.     register struct request *r = &request;
  1160.     time_t start;
  1161.     struct stat statb;
  1162.     long kb;
  1163.     if((f = open(local, 0)) == -1) {
  1164.         perror(local);
  1165.         return(-1);
  1166.     }
  1167.     n = sprintf(r->r_data, "%d %s %s", fmode(local), local, remote);
  1168.     printf("%s: ", local); fflush(stdout);
  1169.     send(r, n, PUTFILE, server);
  1170.     recv(r);
  1171.     stat(local, &statb);
  1172.     start = time((long *) 0);
  1173.     kb = kbytes(statb.st_size);
  1174.     if(r->r_type == ACCEPT) {
  1175.         printf("[sending] "); fflush(stdout);
  1176.         if(ftpout(f, server)) 
  1177.             printf("OK (%d kb/sec)\n", kb / interval(start));
  1178.     }
  1179.     else printf("[%s] %s\n", hostname, r->r_data);
  1180.     close(f);
  1181.     return(0);
  1182. }
  1183. /*
  1184.  * getit: persuade remote host to send a file.
  1185.  */
  1186. getit(remote, local)
  1187. char *local, *remote;
  1188. {
  1189.     register struct request *r = &request;
  1190.     register int n, f;
  1191.     int mode;
  1192.     char fname[128];
  1193.     time_t start;
  1194.     struct stat statb;
  1195.     long kb;
  1196.     int newfile;
  1197.     strcpy(fname, local);
  1198.     if(stat(local, &statb) != -1) {
  1199.         if((statb.st_mode & S_IFMT) == S_IFDIR)
  1200.             sprintf(fname, "%s/%s", local, lastpart(remote));
  1201.         newfile = 0;
  1202.     }
  1203.     else newfile = 1;
  1204.     if((f = creat(fname, 0666)) == -1) {
  1205.         perror(fname);
  1206.         return(-1);
  1207.     }
  1208.     n = sprintf(r->r_data, "%s", remote);
  1209.     printf("%s: ", fname); fflush(stdout);
  1210.     send(r, n, SENDFILE, server);
  1211.     recv(r);
  1212.     if(r->r_type == ACCEPT) {
  1213.         sscanf(r->r_data, "%d", &mode);
  1214.         if(newfile) chmod(fname, mode);
  1215.         start = time((long *) 0);
  1216.         printf("[receiving] "); fflush(stdout);
  1217.         if(ftpin(f, server, &kb))
  1218.             printf("OK (%d kb/sec)\n", kbytes(kb) / interval(start));
  1219.         else unlink(fname);
  1220.         close(f);
  1221.         return(0);
  1222.     }
  1223.     else {
  1224.         printf("rejected [%s: %s]\n", hostname, r->r_data);
  1225.         close(f);
  1226.         return(-1);
  1227.     }
  1228. }
  1229. long kbytes(size)
  1230. long size;
  1231. {
  1232.     size >>= 10;
  1233.     return(size ? size : 1);
  1234. }
  1235. long interval(start)
  1236. {
  1237.     long i = time((long *) 0) - start;
  1238.     return(i ? i : 1);
  1239. }
  1240. SHAR_EOF
  1241. if test 5486 -ne "`wc -c < 'rcp.c'`"
  1242. then
  1243.     echo shar: "error transmitting 'rcp.c'" '(should have been 5486 characters)'
  1244. fi
  1245. fi
  1246. echo shar: "extracting 'eth.c'" '(3689 characters)'
  1247. if test -f 'eth.c'
  1248. then
  1249.     echo shar: "will not over-write existing file 'eth.c'"
  1250. else
  1251. cat << \SHAR_EOF > 'eth.c'
  1252. /*
  1253.  * Copyright (C) 1988 Dave Settle. All rights reserved.
  1254.  * Permission is granted to use, copy and modify this software, providing
  1255.  * that it is not sold for profit, and that this copyright notice is retained
  1256.  * in any copies of the source.
  1257.  */
  1258. /*
  1259.  * eth.c: various useful routines to talk to the ethernet.
  1260.  */
  1261. #include <sys/types.h> 
  1262. #include <sys/errno.h>
  1263. #include <stdio.h>
  1264.  
  1265. #include "ni.h"
  1266.  
  1267. #define MINBUF    64 + 64        /* min 128 bytes data */
  1268.  
  1269. char *panicstr;            /* cause (if any) of a SIGTERM */
  1270.  
  1271. /*
  1272.  * configure the 'ethernet' fd so that we appear as node 'thisnode'
  1273.  * Allocate 'nbufs' to cope with expected data.
  1274.  */
  1275. configure(thisnode, nbufs, pid)
  1276. char *thisnode;
  1277. {
  1278.     if((ethernet = open("/dev/ni", 2)) == -1) {
  1279.         perror("/dev/ni");
  1280.         return(-1);
  1281.     }
  1282.     if(ioctl(ethernet, NIGETA, &port)) {
  1283.         perror("NIGETA");
  1284.         return(-1);
  1285.     }
  1286.     thisnode[NODE] = port.srcaddr[LSB];
  1287.     thisnode[PIDMSB] = (pid & 0xff00) >> 8;
  1288.     thisnode[PIDLSB] = pid & 0xff;
  1289. #ifdef DEBUG
  1290.     printf("Running on node %s\n", ipaddr(port.srcaddr));
  1291. #endif
  1292.      port.type = ETHERTYPE;
  1293.      if(nbufs) port.rcvq_sz = nbufs;
  1294.     port.rcvb_sz = (sizeof (struct request) + 3) & ~3;
  1295.      memcpy(port.srcaddr, thisnode, ETHERSIZE);
  1296.      port.protocol = PROTOCOL;
  1297.      if(ioctl(ethernet, NISETA, &port)) {
  1298.          perror("NISETA");
  1299.          return(-1);
  1300.      }
  1301. #ifdef DEBUG
  1302.      printf("Port configured as node %s\n", ipaddr(thisnode));
  1303. #endif
  1304.      memcpy(mynode, thisnode, ETHERSIZE);
  1305.      return(0);
  1306. }
  1307. /*
  1308.  * recv: receive a request from the ethernet
  1309.  * If it's a request to terminate, propagate a SIGTERM signal.
  1310.  * This means that users of this routine can decide on what action to take,
  1311.  * and also be sure that no TERMINATE packets will not be detected.
  1312.  */
  1313. recv(r)
  1314. register struct request *r;
  1315. {
  1316.     if(read(ethernet, r, sizeof (struct request)) == -1) {
  1317.         r->r_type = UNDEFINED;
  1318.         r->r_size = 0;
  1319.         return(-1);
  1320.     }
  1321.     if(r->r_type == TERMINATE) {
  1322.         panicstr = r->r_data;
  1323.         skill(getpid(), SIGTERM);
  1324.     }
  1325.     
  1326.     return(0);
  1327. }
  1328. /*
  1329.  * send request to specified node
  1330.  */
  1331. send(r, size, type, node)
  1332. struct request *r;
  1333. char *node;
  1334. {
  1335.     int total, min = sizeof (EI_PORT) + 52;
  1336.     memcpy(r->r_port.srcaddr, mynode, ETHERSIZE);
  1337.     memcpy(r->r_port.destaddr, node, ETHERSIZE);
  1338.     memcpy(r->r_port.ptype, myprotocol, 2);
  1339.     r->r_type = type;
  1340.     r->r_size = size;
  1341.     total = sizeof (struct request) - ((sizeof r->r_data) - size);
  1342.     total = total > min ? total : min;
  1343.     if(write(ethernet, r, total) == -1) {
  1344.         perror("write error");
  1345.         printf("Packet: src %s ", ipaddr(r->r_port.srcaddr));
  1346.         printf("dest %s\n", ipaddr(r->r_port.destaddr));
  1347.         return(-1);
  1348.     }
  1349.     return(0);
  1350. }
  1351. /*
  1352.  * perror
  1353.  */
  1354. extern char *sys_errlist[];
  1355. extern int errno;
  1356. extern int sys_nerr;
  1357.  
  1358. char *ni_error[] = {
  1359.     "Bad network address",
  1360.     "Bad port configuration specification",
  1361.     "Port not configured",
  1362.     "Open failure - out of memory?",
  1363.     "HLP circuit failure",
  1364.     "HLP fault",
  1365.     "Port not available",
  1366.     "Network not available",
  1367.     "Driver fault",
  1368.     "Hardware failure",
  1369.     "Network fault",
  1370.     "Bad packet",
  1371.     "Device has been reset"
  1372. };
  1373.  
  1374. perror(msg)
  1375. char *msg;
  1376. {
  1377.     char *e;
  1378.     if((errno > 200) && (errno < 213)) e = ni_error[errno - 200];
  1379.     else e = sys_errlist[errno];
  1380.     fprintf(stderr, "%s: %s [%d]\n", msg, e, errno);
  1381. }
  1382. /*
  1383.  * used only with 'sdb'
  1384.  */
  1385. dump(s)
  1386. char *s;
  1387. {
  1388.     printf("%s\n", ipaddr(s));
  1389. }
  1390. hostaddr(host)
  1391. char *host;
  1392. {
  1393.     FILE *map;
  1394.     char name[32];
  1395.     int id;
  1396.     if((map = fopen(MAP, "r")) == NULL) return(0);
  1397.     while(fscanf(map, "%s%x", name, &id) != EOF) 
  1398.         if(!strcmp(name, host)) {
  1399.             fclose(map);
  1400.             return(id);
  1401.         }
  1402.     fclose(map);
  1403.     return(-1);
  1404. }
  1405. /*
  1406.  * skill: 'safe' kill. There's a bug somewhere when we kill proc 0 and log
  1407.  * everyone out. This is a wrokaround.
  1408.  */
  1409. skill(proc, sig){
  1410.     if(proc < 1) {
  1411.         errno = EINVAL;
  1412.         return(-1);
  1413.     }
  1414.     return(kill(proc, sig));
  1415. }
  1416.  
  1417. SHAR_EOF
  1418. if test 3689 -ne "`wc -c < 'eth.c'`"
  1419. then
  1420.     echo shar: "error transmitting 'eth.c'" '(should have been 3689 characters)'
  1421. fi
  1422. fi
  1423. echo shar: "extracting 'ipaddr.c'" '(466 characters)'
  1424. if test -f 'ipaddr.c'
  1425. then
  1426.     echo shar: "will not over-write existing file 'ipaddr.c'"
  1427. else
  1428. cat << \SHAR_EOF > 'ipaddr.c'
  1429. /*
  1430.  * Copyright (C) 1988 Dave Settle. All rights reserved.
  1431.  * Permission is granted to use, copy and modify this software, providing
  1432.  * that it is not sold for profit, and that this copyright notice is retained
  1433.  * in any copies of the source.
  1434.  */
  1435. /*
  1436.  * ipaddr: print address as xx.xx.xx.xx.xx.xx
  1437.  */
  1438. char *ipaddr(a)
  1439. char *a;
  1440. {
  1441.     register int i;
  1442.     static char s[26];
  1443.     for(i=0;i<5;i++) sprintf(s + (i * 3), "%02x.", a[i]);
  1444.     sprintf(s + 15, "%02x", a[5]);
  1445.     return(s);
  1446. }
  1447.  
  1448. SHAR_EOF
  1449. if test 466 -ne "`wc -c < 'ipaddr.c'`"
  1450. then
  1451.     echo shar: "error transmitting 'ipaddr.c'" '(should have been 466 characters)'
  1452. fi
  1453. fi
  1454. echo shar: "extracting 'talk.c'" '(1303 characters)'
  1455. if test -f 'talk.c'
  1456. then
  1457.     echo shar: "will not over-write existing file 'talk.c'"
  1458. else
  1459. cat << \SHAR_EOF > 'talk.c'
  1460. /*
  1461.  * talk.c: routines to talk to the remote shell, run on the remote system
  1462.  */
  1463. #include "ni.h"
  1464.  
  1465.  
  1466. passon(r, output)
  1467. register struct request *r;    /* stuff to send        */
  1468. int output;            /* file descriptor to write on    */
  1469. {
  1470.     int ok = 1;
  1471.     switch(r->r_type) {
  1472.     case TERMINATE:
  1473.         ok = 0;
  1474.         break;
  1475.     case RMTSIGINT:
  1476.         kill(0, SIGINT);
  1477.         break;
  1478.     case RMTSIGQUIT:
  1479.         kill(0, SIGQUIT);
  1480.         break;
  1481.     case DATA:
  1482.         write(output, r->r_data, r->r_size);
  1483.         break;
  1484.     default:
  1485.         sprintf(r->r_data, "Bad packet type %d\n", r->r_type);
  1486.             send(r, strlen(r->r_data), DATA, client);
  1487.     }
  1488.     return(ok);
  1489. }
  1490. #define PTTY "/dev/ptc"
  1491. #define MAXPTTYS 8
  1492. /*
  1493.  * special case routine - use the PC-Interface pseudo-ttys to get a real
  1494.  * remote login!
  1495.  */
  1496. dologin(r)
  1497. register struct request *r;
  1498. {
  1499.     int dev;            /* file descriptor    */
  1500.     int i, n, reader;
  1501.     char name[sizeof PTTY + 4];
  1502.     for(i=0;i<MAXPTTYS;i++) {
  1503.         sprintf(name, "%s%02d", PTTY, i);
  1504.         if((dev = open(name, 2)) != -1) break;
  1505.     }
  1506.     if(dev == -1) {
  1507.         n = sprintf(r->r_data, "No PC-Interface ports available");
  1508.         send(r, n, TERMINATE, client);
  1509.         exit(1);
  1510.     }
  1511.     if(reader = fork()) {
  1512.         while(1) {
  1513.             recv(r);
  1514.             if(passon(r, dev) == 0) {
  1515.                 skill(reader, SIGTERM);
  1516.                 exit(0);
  1517.             }
  1518.         }
  1519.     }
  1520.     else {
  1521.         while(1) {
  1522.             n = read(dev, r->r_data, sizeof r->r_data);
  1523.             send(r, n, DATA, client);
  1524.         }
  1525.     }
  1526. }
  1527.  
  1528.         
  1529. SHAR_EOF
  1530. if test 1303 -ne "`wc -c < 'talk.c'`"
  1531. then
  1532.     echo shar: "error transmitting 'talk.c'" '(should have been 1303 characters)'
  1533. fi
  1534. fi
  1535. echo shar: "extracting 'sub.c'" '(1703 characters)'
  1536. if test -f 'sub.c'
  1537. then
  1538.     echo shar: "will not over-write existing file 'sub.c'"
  1539. else
  1540. cat << \SHAR_EOF > 'sub.c'
  1541. /*
  1542.  * sub.c: general subroutines for "rcp"
  1543.  * remote(s): check if "s" refers to a local(0) or remote(1) file.
  1544.  * sysname(s): return systemname assosciated with s. "localnode" returned
  1545.  *         if there is no specific "sys!" prefix.
  1546.  * filename(s): return filname part - strip first system prefix, if there is one
  1547.  * 
  1548.  * fmode(s): return value of file mode.
  1549.  *
  1550.  * lastpart(f): return last entry of path 'f'
  1551.  */
  1552.  
  1553. #define rindex strrchr
  1554. #define index strchr
  1555.  
  1556. char *filename(), *sysname(), *lastpart(), *malloc(), 
  1557.     *strrchr(), *strchr();
  1558. extern char *sys_errlist[];
  1559. #include "errno.h"
  1560. #include <stdio.h>
  1561. #include <time.h>
  1562. #include <sys/types.h>
  1563. #include <sys/stat.h>
  1564.  
  1565. #include "ni.h"
  1566. long time();
  1567. char *asctime();
  1568. struct tm *localtime();
  1569.  
  1570. remote(s)
  1571. char *s;
  1572. {
  1573.     if(filename(s) == s) return(0);
  1574.     else return(1);
  1575. }
  1576. /*
  1577.  * sysname(f): get system name part of file name.
  1578.  * if no system present, return localnode
  1579.  */
  1580. char *sysname(f)
  1581. char *f;
  1582. {
  1583.     register char *p, *sys;
  1584.     register int length;
  1585.     if((p = filename(f)) == f) return(localnode);
  1586.     else {
  1587.         sys = malloc(length = p - f);
  1588.         strncpy(sys, f, length - 1);
  1589.         sys[length - 1] = 0;
  1590.         return(sys);
  1591.     }
  1592. }
  1593. /*
  1594.  * filename(f): return file name part of f
  1595.  * Strips the first (and only the first) system, if there is one.
  1596.  * [8] Also allow ':' as system flag.
  1597.  */
  1598. char *filename(f)
  1599. register char *f;
  1600. {
  1601.     register char *s;
  1602.     if((s = index(f, '!')) || (s = index(f, ':'))) return(++s);
  1603.     else return(f);
  1604. }
  1605. bang(c)
  1606. char c;
  1607. {
  1608.     if((c == '!') || (c == ':')) return(1);
  1609.     else return(0);
  1610. }
  1611.  
  1612. fmode(f)
  1613. char *f;
  1614. {
  1615.     struct stat s;
  1616.     if(stat(f, &s) == -1) return(0664);
  1617.     return(s.st_mode);
  1618. }
  1619.  
  1620. char *lastpart(f)
  1621. char *f;
  1622. {
  1623.     char *l;
  1624.     if(l = rindex(f, '/')) return(++l);
  1625.     else return(f);
  1626. }
  1627.     
  1628. SHAR_EOF
  1629. if test 1703 -ne "`wc -c < 'sub.c'`"
  1630. then
  1631.     echo shar: "error transmitting 'sub.c'" '(should have been 1703 characters)'
  1632. fi
  1633. fi
  1634. echo shar: "extracting 'file.c'" '(2159 characters)'
  1635. if test -f 'file.c'
  1636. then
  1637.     echo shar: "will not over-write existing file 'file.c'"
  1638. else
  1639. cat << \SHAR_EOF > 'file.c'
  1640. /*
  1641.  * file.c: server end of the "rcp" function.
  1642.  * Send or get files. Relies on the transport interface to detect errors,
  1643.  * only checks sequence numbers to identify lost packets.
  1644.  * If a packet is lost, then the transfer is abandoned and restarted - this
  1645.  * is not expected to happen often.
  1646.  */
  1647. #include <sys/types.h>
  1648. #include <sys/stat.h>
  1649.  
  1650. #include "ni.h"
  1651.  
  1652. #include "ftp.h"
  1653.  
  1654. extern char *sys_errlist[];
  1655. extern int errno;
  1656.  
  1657. char *lastpart();
  1658.  
  1659. fileserver(r)
  1660. register struct request *r;
  1661. {
  1662.     register int f, n;
  1663.     int mode, newfile;
  1664.     long kb;
  1665.     struct stat statb;
  1666.     char fname[128], local[128], remote[128];
  1667. /*
  1668.  * re-configure the ethernet port to handle sufficient buffers
  1669.  */
  1670.     close(ethernet);
  1671.     configure(server, WINDOW, getpid());
  1672.     send(r, 0, ACCEPT, client);
  1673.     signal(SIGTERM, SIG_DFL);        /* TERMINATE packet kills */
  1674.     while(1) {
  1675.         recv(r);            /* get request */
  1676.         switch(r->r_type) {
  1677.         case TERMINATE:
  1678.             exit(1);
  1679.             break;
  1680.         case SENDFILE:
  1681.             strncpy(fname, r->r_data, r->r_size);
  1682.             fname[r->r_size] = 0;
  1683.             if((f = open(fname, 0)) == -1) {
  1684.                 n = sprintf(r->r_data, "%s: %s", fname,
  1685.                     sys_errlist[errno]);
  1686.                 send(r, n, REJECT, client);
  1687.             }
  1688.             else {
  1689.                 stat(fname, &statb);
  1690.                 n = sprintf(r->r_data, "%d", statb.st_mode & 0777);
  1691.                 send(r, n, ACCEPT, client);
  1692.                 ftpout(f, client);
  1693.                 close(f);
  1694.             }
  1695.             break;
  1696.         case PUTFILE:
  1697.             n = sscanf(r->r_data,  "%d %s %s", 
  1698.                     &mode,  remote, local);
  1699.             if(n != 3) {
  1700.                 n = sprintf(r->r_data, "Illegal request parameters");
  1701.                 send(r, n, REJECT, client);
  1702.                 break;
  1703.             }
  1704.             strcpy(fname, local);
  1705.             if(stat(local, &statb) != -1) {
  1706.                 if((statb.st_mode & S_IFMT) == S_IFDIR)
  1707.                     sprintf(fname, "%s/%s", local, 
  1708.                         lastpart(remote));
  1709.                 newfile = 0;
  1710.             }
  1711.             else newfile = 1;
  1712.             if((f = creat(fname, 0666)) == -1) {
  1713.                 n = sprintf(r->r_data, "%s: %s", fname,
  1714.                     sys_errlist[errno]);
  1715.                 send(r, n, REJECT, client);
  1716.             }
  1717.             else {
  1718.                 if(newfile) chmod(fname, mode);
  1719.                 send(r, 0, ACCEPT, client);
  1720.                 if(!ftpin(f, client, &kb)) unlink(fname);
  1721.                 close(f);
  1722.             }
  1723.             break;
  1724.         default:
  1725.             n = sprintf(r->r_data, "Illegal request function %d", 
  1726.                     r->r_type);
  1727.             send(r, n, REJECT, client);
  1728.         }
  1729.     }
  1730. }
  1731. SHAR_EOF
  1732. if test 2159 -ne "`wc -c < 'file.c'`"
  1733. then
  1734.     echo shar: "error transmitting 'file.c'" '(should have been 2159 characters)'
  1735. fi
  1736. fi
  1737. echo shar: "extracting 'terminate.c'" '(512 characters)'
  1738. if test -f 'terminate.c'
  1739. then
  1740.     echo shar: "will not over-write existing file 'terminate.c'"
  1741. else
  1742. cat << \SHAR_EOF > 'terminate.c'
  1743. #include <sys/signal.h>
  1744.  
  1745. #define GRACETIME 1
  1746. /*
  1747.  * terminate: log out the child shell. Sends SIGHUP, SIGTERM, SIGKILL
  1748.  * until the child exits.
  1749.  */
  1750. terminate(child)
  1751. {
  1752.     int status;
  1753.     if(child < 1) return(0);
  1754.     kill(child, SIGHUP);
  1755.     alarm(GRACETIME);
  1756.     if(wait(&status) == -1) {
  1757.         kill(child, SIGTERM);
  1758.         alarm(GRACETIME);
  1759.         if(wait(&status) == -1) {
  1760.             kill(child, SIGKILL);
  1761.             alarm(GRACETIME);
  1762.             if(wait(&status) == -1) {
  1763.                 printf("Cannot kill child pid %d\n", child);
  1764.                 status = 0;
  1765.             }
  1766.         }
  1767.     }
  1768.     alarm(0);
  1769. }
  1770.  
  1771. SHAR_EOF
  1772. if test 512 -ne "`wc -c < 'terminate.c'`"
  1773. then
  1774.     echo shar: "error transmitting 'terminate.c'" '(should have been 512 characters)'
  1775. fi
  1776. fi
  1777. echo shar: "extracting 'mynode.c'" '(627 characters)'
  1778. if test -f 'mynode.c'
  1779. then
  1780.     echo shar: "will not over-write existing file 'mynode.c'"
  1781. else
  1782. cat << \SHAR_EOF > 'mynode.c'
  1783. /*
  1784.  * Copyright (C) 1988 Dave Settle. All rights reserved.
  1785.  * Permission is granted to use, copy and modify this software, providing
  1786.  * that it is not sold for profit, and that this copyright notice is retained
  1787.  * in any copies of the source.
  1788.  */
  1789. /*
  1790.  * mynode.c: print physical ethernet address of the local node.
  1791.  */
  1792.  
  1793. #include <sys/ni.h>
  1794.  
  1795. main(){
  1796.     NI_PORT port;
  1797.     int ethernet;
  1798.     if((ethernet = open("/dev/ni", 2)) == -1) {
  1799.         perror("/dev/ni");
  1800.         exit(1);
  1801.     }
  1802.     if(ioctl(ethernet, NIGETA, &port)) {
  1803.         perror("NIGETA");
  1804.         exit(1);
  1805.     }
  1806.     printf("Physical ethernet address: %s\n", ipaddr(port.srcaddr));
  1807.     close(ethernet);
  1808.     exit(0);
  1809. }
  1810. SHAR_EOF
  1811. if test 627 -ne "`wc -c < 'mynode.c'`"
  1812. then
  1813.     echo shar: "error transmitting 'mynode.c'" '(should have been 627 characters)'
  1814. fi
  1815. fi
  1816. echo shar: "extracting 'ftp.c'" '(7077 characters)'
  1817. if test -f 'ftp.c'
  1818. then
  1819.     echo shar: "will not over-write existing file 'ftp.c'"
  1820. else
  1821. cat << \SHAR_EOF > 'ftp.c'
  1822. /*
  1823.  * ftp.c: the 'file transfer' protocols.
  1824.  * Sliding-window protocol with selective retransmit.
  1825.  */
  1826. #include "ni.h"
  1827.  
  1828. #include "ftp.h"
  1829.  
  1830. extern char *sys_errlist[];
  1831. int debug;
  1832.  
  1833. #define NULL 0
  1834. #define slotno(num) ((num) % WINDOW)
  1835.  
  1836. struct request buffer[WINDOW];
  1837. struct request *packet[WINDOW];
  1838. int retry = 0;
  1839. sequence_t lower, upper, num;
  1840.  
  1841. wakeup(){
  1842.     signal(SIGALRM, wakeup);
  1843. /*
  1844.  * ftpout: send the file to the indicated host.
  1845.  */
  1846. ftpout(f, addr)
  1847. char *addr;
  1848. {
  1849.     struct request ackreq, *ack = &ackreq;
  1850.     register struct request *r;
  1851.     register int n;
  1852.     int i, eof = 0;
  1853.     lower = upper = num = 0;
  1854.     retry = 0;
  1855.     for(i=0;i<WINDOW;i++) packet[i] = NULL;
  1856.     wakeup();
  1857.     while(1) {
  1858. /*
  1859.  * If the window is not sent, send another packet.
  1860.  * If last packet was null, then all data has been sent.
  1861.  */
  1862.         while(!fullwindow(lower, upper) && !eof) {
  1863.              num = upper++;
  1864.              r = &buffer[slotno(num)];
  1865.              packet[slotno(num)] = r;
  1866.             r->r_sequence = num;
  1867.             n = read(f, r->r_data, sizeof r->r_data);
  1868. /*
  1869.  * read error causes premature EOF
  1870.  */
  1871.             if(n == -1) n = 0;
  1872.             if(debug) 
  1873.                 printf("Sending packet %d\n", r->r_sequence);
  1874. /*
  1875.  * Reset the counter for ack timeouts, then send out the block
  1876.  */
  1877.             retry = 0;
  1878.             send(r, n, DATA, addr);
  1879.             if(n == 0) eof = 1;
  1880.         }
  1881. /* 
  1882.  * We have to wait for an ACK from the other end ...
  1883.  * If it's an ACK, then the sequence field determines which packet is OK
  1884.  * If it's a NACK, then the data is a list of packets which 
  1885.  * must have got lost somewhere. so we re-transmit them.
  1886.  */
  1887.         if(debug) printf("Window full - waiting for ack\n");
  1888.          alarm(TRANSIT);
  1889.         recv(ack);
  1890.         alarm(0);
  1891.         switch(ack->r_type) {
  1892. /*
  1893.  * ACK - mark the packet as sent (delete pointer).
  1894.  * Move lower bound of window up as far as possible.
  1895.  */
  1896.         case ACCEPT:
  1897.             if(debug)
  1898.                 printf("Got ack on packet %d\n", ack->r_sequence);
  1899.             release(ack->r_sequence);
  1900.             break;
  1901.         case REJECT:
  1902.             if(debug)
  1903.                 printf("Got reject (ack packet %d)\n", ack->r_sequence);
  1904.             release(ack->r_sequence);
  1905.             for(i=0;i<ack->r_size;i++) {
  1906.                 if(debug) 
  1907.                     printf("\tpacket %d\n", ack->r_data[i]);
  1908.                 r = packet[slotno(ack->r_data[i])];
  1909.                 if(inwindow(ack->r_data[i], lower, upper))
  1910.                     send(r, r->r_size, DATA, addr);
  1911.             }
  1912.             break;
  1913. /*
  1914.  * Timeout waiting for ACK - retransmit all the outstanding packets.
  1915.  */
  1916.         case UNDEFINED:
  1917.             printf("Timeout\n");
  1918.             if(retry++ > MAXRETRY) {
  1919.                 printf("Too many timeouts - ftp abort\n");
  1920.                 n = sprintf(ack->r_data, "Too many timeouts");
  1921.                 send(ack, n, TERMINATE, addr);
  1922.                 exit(0);
  1923.             }
  1924.             for(i=0;i<WINDOW;i++) if(packet[i])
  1925.                 send(packet[i], packet[i]->r_size, DATA, addr);
  1926.             break;
  1927.          default:
  1928.             printf("Illegal ack type %d\n", ack->r_type);
  1929.         } /* end switch */
  1930. /*
  1931.  * If we've transmitted all the data, and got acks back for all the packets
  1932.  * then the transfer is complete.
  1933.  */
  1934.          if(eof && debug) 
  1935.              printf("EOF - waiting to clear %d - %d\n", lower, upper);
  1936.         if(eof && (upper == lower)) return(1);
  1937.     } /* end data loop */
  1938. }
  1939. /*
  1940.  * ftpin: recv data, plonk in file.
  1941.  * Packet with data size 0 ends file transmission.
  1942.  */
  1943. ftpin(f, addr, kb)
  1944. char *addr;
  1945. long *kb;            /* data size received */
  1946. {
  1947.     register struct request *r;
  1948.     long count = 0;
  1949.     int i, eof = 0;
  1950.     struct request ackreq, *ack = &ackreq;
  1951.     register int n;
  1952.     sequence_t slot, datablock, last, diskblock = 0;
  1953.     lower = num = 0;
  1954.     upper = WINDOW - 1;
  1955.     datablock = 0;
  1956.     for(i=0;i<WINDOW;i++) packet[i] = NULL;
  1957.     while(1) {
  1958. /*
  1959.  * Zero received buffer space, so we know what has got here
  1960.  */
  1961.         retry = 0;
  1962.         if(debug) 
  1963.             printf("Waiting for %d - %d, using buffer %d\n", 
  1964.                 lower, lower + WINDOW, slotno(datablock));
  1965.         r = &buffer[slotno(datablock++)];    /* get a buffer */
  1966.         recv(r);
  1967.         switch(r->r_type) {
  1968. /*
  1969.  * Normal data - fix in the correct place
  1970.  * If we missed packets in the sequence, ask for them again.
  1971.  */
  1972.         case DATA:
  1973.             upper = lower + WINDOW - 1;
  1974.             if(debug) printf("Received packet %d, size %d\n", 
  1975.                 r->r_sequence, r->r_size);
  1976. /*
  1977.  * incoming packet not within our current window. The ack for a previous
  1978.  * packet must have got lost - send it again!
  1979.  * If the packet doesn't seem to fit anywhere, complain!
  1980.  */
  1981.             if(!inwindow(r->r_sequence, lower, upper)) {
  1982.                 if(inwindow(r->r_sequence, lower - WINDOW, lower)) {
  1983.                     ack->r_sequence = r->r_sequence;
  1984.                     send(ack, 0, ACCEPT, addr);
  1985.                 }
  1986.                 else {
  1987.                     if(debug) printf("packet %d out of bounds %d - %d\n",
  1988.                     r->r_sequence, lower, upper);
  1989.                     n = sprintf(ack->r_data, "protocol violation: packet %d arrived in window %d - %d\n",
  1990.                         r->r_sequence, lower, upper);
  1991.                     send(ack, n, TERMINATE, addr);
  1992.                     exit(0);
  1993.                 }
  1994.                 datablock--;
  1995.                 continue;        /* out of window */
  1996.             }
  1997.             slot = slotno(r->r_sequence);
  1998.             if(packet[slot]) {
  1999.                 if(debug) printf("Duplicate - ignored\n");
  2000.                 datablock--;
  2001.                 continue;    /* duplicate */
  2002.             }
  2003.             packet[slot] = r;
  2004.             count += r->r_size;
  2005. /*
  2006.  * Check for eof, and send ack for this packet.
  2007.  */
  2008.             if(r->r_size == 0) {
  2009.                 eof = 1;
  2010.                 last = r->r_sequence;
  2011.             }
  2012.             ack->r_sequence = r->r_sequence;
  2013.             send(ack, 0, ACCEPT, addr);
  2014. /*
  2015.  * Write as many packets as possible.
  2016.  */
  2017.              while(r = packet[slotno(lower)]) {
  2018.                  if(debug) 
  2019.                     printf("Packet %d written to disk\n", r->r_sequence);
  2020.                 if(r->r_sequence != diskblock++) {
  2021.                     printf("Bug detected! data block %d written as disk block %d\n",
  2022.                         r->r_sequence, datablock - 1);
  2023.                     n = sprintf(ack->r_data, "data block sequence error");
  2024.                     send(ack, n, TERMINATE, addr);
  2025.                     exit(1);
  2026.                 }
  2027.                  if(write(f, r->r_data, r->r_size) != r_size) {
  2028.                      perror("write error");
  2029.                      n = sprintf(ack->r_data, "write error: %s",
  2030.                          sys_errlist[errno]);
  2031.                      send(ack, n, TERMINATE, addr);
  2032.                      exit(1);
  2033.                  }
  2034.                  packet[slotno(lower)] = NULL;
  2035.                  lower++;
  2036.              }
  2037.             break;
  2038.         default:
  2039.             printf("Illegal ftp packet %d\n", r->r_type);
  2040.         }    /* end switch */
  2041.         if(eof && (lower == (last + 1))) {
  2042.             *kb = count;
  2043.             return(1);
  2044.         }
  2045.     } /* end data loop */
  2046. }
  2047. /*
  2048.  * Check if 'num' falls between lbound and ubound
  2049.  */
  2050. inwindow(number, lbound, ubound)
  2051. sequence_t number, lbound, ubound;
  2052. {
  2053.     while(1) {
  2054.         if(number == lbound) return(1);
  2055.         if(lbound == ubound) return(0);
  2056.         else lbound++;
  2057.     }
  2058. }
  2059. /*
  2060.  * retransmit: we were looking for packet 'last', but got packet 'this'
  2061.  * Ask for retransmit of intervening packets - this also acks packet 'this'
  2062.  */
  2063. retransmit(last, this, ack, addr)
  2064. sequence_t last, this;
  2065. register struct request *ack;
  2066. char *addr;
  2067. {
  2068.     int j, n = 0;
  2069.     for(j=0;j<WINDOW;j++) if(packet[j] == NULL)
  2070.         ack->r_data[n++] = (sequence_t) lower + j;
  2071.     ack->r_sequence = this;
  2072.     send(ack, n, REJECT, addr);
  2073. }
  2074. /*
  2075.  * release: mark packet 'n' as being succesfully received.
  2076.  * Move lower bound of window up, if possible.
  2077.  */
  2078. release(n)
  2079. sequence_t n;
  2080. {
  2081.     packet[slotno(n)] = NULL;
  2082.     while(packet[slotno(lower)] == NULL) {
  2083.         lower++;
  2084.         if(lower == upper) break;
  2085.     }
  2086.     if(debug) printf("Packet %d released - lbound now %d\n", n, lower);
  2087. }
  2088. /*
  2089.  * fullwindow: if distance between lower and upper is >= WINDOW
  2090.  */
  2091. fullwindow(lower, upper)
  2092. register sequence_t lower, upper;
  2093. {
  2094.     register int n = 0;
  2095.     while(lower++ != upper) n++;
  2096.     if(n >= WINDOW) return(1);
  2097.     return(0);
  2098. }
  2099. SHAR_EOF
  2100. if test 7077 -ne "`wc -c < 'ftp.c'`"
  2101. then
  2102.     echo shar: "error transmitting 'ftp.c'" '(should have been 7077 characters)'
  2103. fi
  2104. fi
  2105. echo shar: "extracting 'ni.h'" '(1591 characters)'
  2106. if test -f 'ni.h'
  2107. then
  2108.     echo shar: "will not over-write existing file 'ni.h'"
  2109. else
  2110. cat << \SHAR_EOF > 'ni.h'
  2111. #include <sys/ni.h>
  2112. #include <sys/signal.h>
  2113.  
  2114. #ifdef MAIN
  2115. #define EXTERN
  2116. #else
  2117. #define EXTERN extern
  2118. #endif
  2119.  
  2120. #define MAP "/usr/lib/ethernet.addr"
  2121.  
  2122. EXTERN NI_PORT port;
  2123. EXTERN int debug;
  2124. EXTERN char *hostname, localnode[16];
  2125.  
  2126. #define ETHERSIZE 6
  2127.  
  2128. typedef char address_t[];
  2129. typedef unsigned char sequence_t;
  2130.  
  2131. #define PORT 4            /* plonk port id here */
  2132. #define NODE 5            /* plonk lsb node address */
  2133. #define LSB 5            /* lsb of node address */
  2134. #define PIDMSB 3
  2135. #define PIDLSB 4
  2136.  
  2137. EXTERN int ethernet;
  2138.  
  2139.  
  2140.  
  2141. #ifdef MAIN
  2142. char server[] = {0x0f, 0x02, 0x02, 0, 0, NODE};
  2143. #else
  2144. extern char server[];
  2145. #endif
  2146.  
  2147.  
  2148. #ifdef MAIN
  2149. char client[] = {0x0f, 0x02, 0x02, PIDMSB, PIDLSB, NODE};
  2150. #else
  2151. extern char client[];
  2152. #endif
  2153.  
  2154. #define PROTOCOL 0x5656
  2155.  
  2156.  
  2157. #ifdef MAIN
  2158. char myprotocol[] = {0x56, 0x56};
  2159. #else
  2160. extern char myprotocol[];
  2161. #endif
  2162.  
  2163.  
  2164. #ifdef MAIN
  2165. char mynode[] = {0,0,0,0,0,0};
  2166. #else
  2167. extern char mynode[];
  2168. #endif
  2169.  
  2170. char *ipaddr(), *getenv();
  2171.  
  2172. struct request {
  2173.     EI_PORT r_port;            /* Ethernet header */
  2174.     int r_size;            /* size of data field */
  2175.     char r_type;            /* type of request */
  2176.     unsigned char r_sequence;    /* Sequence number */
  2177.     char r_data[1024];        /* data field */
  2178. };
  2179.  
  2180. #define DATA 0            /* just data of some sort */
  2181. #define UNDEFINED 1        /* crappy packet */
  2182. #define REQUEST 2        /* Is this necessary? */
  2183. #define TERMINATE 3        /* STOP! Exit ASAP */
  2184. #define RMTSIGINT 4        /* signal from here to there */
  2185. #define RMTSIGQUIT 5        /* another signal */
  2186. #define ACCEPT 6        /* I accept that file */
  2187. #define REJECT 7        /* I have rejected that file */
  2188. #define PUTFILE 8        /* file to server */
  2189. #define SENDFILE 9        /* file from server */
  2190. SHAR_EOF
  2191. if test 1591 -ne "`wc -c < 'ni.h'`"
  2192. then
  2193.     echo shar: "error transmitting 'ni.h'" '(should have been 1591 characters)'
  2194. fi
  2195. fi
  2196. echo shar: "extracting 'ftp.h'" '(192 characters)'
  2197. if test -f 'ftp.h'
  2198. then
  2199.     echo shar: "will not over-write existing file 'ftp.h'"
  2200. else
  2201. cat << \SHAR_EOF > 'ftp.h'
  2202. /*
  2203.  * Some constants for the ftp
  2204.  */
  2205. #define WINDOW    4        /* Window size */
  2206. #define TRANSIT 2        /* Estimated transit time (sec) for window */
  2207. #define MAXRETRY 5        /* No of timeouts per window */
  2208.  
  2209.  
  2210. SHAR_EOF
  2211. if test 192 -ne "`wc -c < 'ftp.h'`"
  2212. then
  2213.     echo shar: "error transmitting 'ftp.h'" '(should have been 192 characters)'
  2214. fi
  2215. fi
  2216. exit 0
  2217. #    End of shell archive
  2218.  
  2219. --
  2220.  
  2221. Dave Settle, 
  2222.     SMB Business Software, Thorn EMI Datasolve, High St, Mansfield, UK
  2223.  
  2224. [Until 1 May] UUCP:    dave@smb.co.uk
  2225.             ...!mcvax!ukc!nott-cs!smb!dave    
  2226.  
  2227.     
  2228. [After 1 May] UUCP:    (to be announced ...)
  2229.  
  2230.  
  2231.     <--- This way to point of view --->
  2232.  
  2233.